#!/usr/bin/perl -w

use App::Options (
    options => [qw(dbdriver dbclass dbhost dbname dbuser dbpass)],
    option => {
        dbclass  => { default => "App::Repository::MySQL", },
        dbdriver => { default => "mysql", },
        dbhost   => { default => "localhost", },
        dbname   => { default => "test", },
        dbuser   => { default => "", },
        dbpass   => { default => "", },
    },
);

use Test::More qw(no_plan);
use lib "../App-Context/lib";
use lib "../../App-Context/lib";
use lib "lib";
use lib "../lib";
use lib ".";
use lib "t";

use App;
use App::Repository;
use App::RepositoryObject;
use RepositoryTestUtils qw(create_table drop_table populate_table);

package App::Moose::RepositoryObject::Man;
use vars qw($VERSION);
$VERSION = 0.01;
use Moose;
extends "App::Moose::RepositoryObject";

package App::Moose::RepositoryObject::Woman;
use vars qw($VERSION);
$VERSION = 0.01;
use Moose;
extends "App::Moose::RepositoryObject";

package main;

use strict;

if (!$App::options{dbuser}) {
    ok(1, "No dbuser given. Tests assumed OK. (add dbuser=xxx and dbpass=yyy to app.conf in 't' directory)");
    exit(0);
}

my $context = App->context(
    conf_file => "",
    conf => {
        Repository => {
            default => {
                class => $App::options{dbclass},
                dbdriver => $App::options{dbdriver},
                dbhost => $App::options{dbhost},
                dbname => $App::options{dbname},
                dbuser => $App::options{dbuser},
                dbpass => $App::options{dbpass},
                table => {
                    test_person => {
                        class => [
                            [ "gender", "F", "App::Moose::RepositoryObject::Woman" ],
                            # [ undef,  undef, "App::Moose::RepositoryObject::Man" ],  # otherwise Man
                        ],
                        primary_key => ["person_id"],
                    },
                },
            },
        },
    },
    debug_sql => $App::options{debug_sql},
    trace => $App::options{trace},
    repositoryobject_class => $App::options{repositoryobject_class},
);

my $rep = $context->repository();

&drop_table($rep, "test_person");
&create_table($rep, "test_person");
&populate_table($rep, "test_person");

&drop_table($rep, "test_visit");
&create_table($rep, "test_visit");
&populate_table($rep, "test_visit");

&drop_table($rep, "test_city");
&create_table($rep, "test_city");
&populate_table($rep, "test_city");

my $dbtype = $App::options{dbtype} || "mysql";

###########################################################################
# DATA ACCESS TESTS
###########################################################################
my ($person_id, $first_name, $last_name, $address, $city, $state, $zip, $country);
my ($home_phone, $work_phone, $email_address, $gender, $birth_dt, $age);

my $columns = [ "person_id", "age", "first_name", "gender", "state" ];
my $rows = [
    [ 1, 39, "stephen",   "M", "GA", ],
    [ 2, 37, "susan",     "F", "GA", ],
    [ 3,  6, "maryalice", "F", "GA", ],
    [ 4,  3, "paul",      "M", "GA", ],
    [ 5,  1, "christine", "F", "GA", ],
    [ 6, 45, "tim",       "M", "GA", ],
    [ 7, 39, "keith",     "M", "GA", ],
];

my ($row, $nrows);

#####################################################################
#  $value  = $rep->get ($table, $key,     $col,   \%options);
#  $rep->set($table, $key,     $col,   $value,    \%options);
#####################################################################
{
    my $obj = $rep->get_object("test_person", 1);
    isa_ok($obj, "App::Moose::RepositoryObject", "stephen");
    $first_name = $obj->get("first_name");
    is($first_name, "stephen", "get() first_name [$first_name]");
    is($obj->set("first_name", "steve"),1,"set() first name [steve]");
}
{
    my $obj = $rep->get_object("test_person", 1);
    $first_name = $obj->get("first_name");
    is($first_name, "steve", "get() modified first_name [$first_name]");
    $age = $obj->get("age");
    is($age, 39, "get() age");
}
{
    my $obj = $rep->get_object("test_person", 1, []);
    $first_name = $obj->get("first_name");
    is($first_name, "steve", "get() modified first_name [$first_name] from uninit object");
    $age = $obj->get("age");
    is($age, 39, "get() age from uninit object");
}

{
    my $obj = $rep->get_object("test_person", 2, []);
    isa_ok($obj, "App::Moose::RepositoryObject", "susan");
    ok($obj->set(["first_name","age"], ["sue",38]), "set() 2 values");
    ($first_name, $age) = $obj->get(["first_name","age"]);
    is($first_name, "sue", "get() 2 values (checking 1 of 2)");
    is($age, 38, "get() 2 values (checking 2 of 2)");
}

{
    my $obj = $rep->get_object("test_person", 2);
    isa_ok($obj, "App::Moose::RepositoryObject::Woman", "susan");
}

{
    my $objs = $rep->get_objects("test_person", {}, undef, {order_by => "person_id"});
    is($objs->[0]{_key}, 1, "get_objects() automatically set the _key");
    isa_ok($objs->[0], "App::Moose::RepositoryObject", "by get_objects(), stephen");
    isa_ok($objs->[1], "App::Moose::RepositoryObject::Woman", "by get_objects(), susan");
}

{
    my $obj = $rep->get_object("test_person", {}, undef, {order_by => "person_id"});
    is($obj->{_key}, 1, "get_object() automatically set the _key");
}

{
    ok($rep->set_row("test_person", {first_name=>'paul'}, ["age", "state"], [5, "CA"]),"set_row() 2 values w/ %crit");
    my $obj = $rep->get_object("test_person", {first_name=>'paul'}, ["age", "state","person_id"]);
    is($obj->{age},         5, "get_object() 3 values w/ %crit (checking 1 of 3)");
    is($obj->{state},    "CA", "get_object() 3 values w/ %crit (checking 2 of 3)");
    is($obj->{person_id},   4, "get_object() 3 values w/ %crit (checking 3 of 3)");
}

{
    my $obj = $rep->get_object("test_person", 1);
    is($obj->{_key}, 1, "get_object() by key");

    my $retval = $obj->delete();
    ok($retval, "delete() seems to have worked");

    my $obj2 = $rep->get_object("test_person", 1);
    ok(! defined $obj2, "delete() yep, it's really gone");

    $obj2 = $rep->new_object("test_person", $obj);
    is($obj2->{first_name},$obj->{first_name}, "new.first_name seems ok");
    is($obj2->{age},$obj->{age}, "new.age seems ok");
    is($obj2->{_key},$obj->{_key}, "new._key seems ok");

    my $obj3 = $rep->get_object("test_person", 1);
    ok(defined $obj2, "new() it's back");
    is($obj3->{first_name},$obj->{first_name}, "new.first_name seems ok");
    is($obj3->{age},$obj->{age}, "new.age seems ok");
    is($obj3->{_key},$obj->{_key}, "new._key seems ok");

    my $obj4 = $rep->new_object("test_person",{first_name => "christine", gender => "F"});
    is($obj4->{first_name},"christine", "new.first_name (2) seems ok");
    is($obj4->{_key},8, "new._key is ok");
    is($obj4->{person_id},8, "new.person_id is ok");
    isa_ok($obj4, "App::Moose::RepositoryObject::Woman", "by new_object(), christine");

    $obj = { city_cd => "BOS", city_nm => "Boston" };
    $obj2 = $rep->new_object("test_city", $obj);
    isa_ok($obj2, "App::Moose::RepositoryObject", "new_object(city,{BOS})");
    is($obj2->{city_cd},$obj->{city_cd},      "new_object(city,{BOS}).city_cd = [$obj->{city_cd}]");
    is($obj2->{_repository}{name}, "default", "new_object(city,{BOS})._repository = [default]");
    is($obj2->{_table},     "test_city",      "new_object(city,{BOS})._table = [$obj2->{_table}]");
    is($obj2->{_key},       $obj->{city_cd},  "new_object(city,{BOS})._key = [$obj2->{_key}]");
    my $json = "{'_key' : 'BOS', '_repository' : 'default', '_table' : 'test_city', 'arp_nm' : null, 'city_cd' : 'BOS', 'city_nm' : 'Boston', 'country' : null, 'state' : null}";
    is($obj2->TO_JSON(), $json, "new_object(city,{BOS}).TO_JSON = [{...}]");
    $nrows = $obj2->set("arp_nm", "Logan Airport");
    is($nrows, 1, "obj(city)->set(col, value): works");
    $obj3 = $rep->get_object("test_city", "BOS");
    is($obj3->{arp_nm},"Logan Airport", "obj(city).arp_nm = [Logan Airport]");

    $obj = { city_cd => "BOS", person_id => undef };
    eval {
        $obj2 = $rep->new_object("test_visit", $obj);
    };
    ok(($@ ? 1 : 0), "new_object(visit) correctly failed when insufficient initial values given");
    $obj = { city_cd => "BOS", person_id => 1, visit_dt => "1980-08-30" };
    eval {
        $obj2 = $rep->new_object("test_visit", $obj);
    };
    ok(($@ ? 1 : 0), "new_object(visit) correctly failed when primary key violated");
    $obj = { city_cd => "BOS", person_id => 1, visit_dt => "1980-08-31", occasion => "back again" };
    $obj2 = $rep->new_object("test_visit", $obj);
    isa_ok($obj2, "App::Moose::RepositoryObject",    "new_object(visit)");
    is($obj2->{city_cd}, $obj->{city_cd},     "new_object(visit).city_cd = [$obj->{city_cd}]");
    is($obj2->{person_id}, $obj->{person_id}, "new_object(visit).person_id = [$obj->{person_id}]");
    is($obj2->{visit_dt}, $obj->{visit_dt},   "new_object(visit).visit_dt = [$obj->{visit_dt}]");
    is($obj2->{occasion}, $obj->{occasion},   "new_object(visit).occasion = [$obj->{occasion}]");
    $nrows = $obj2->set(["occasion"], ["woke up in the morning"]);
    is($nrows, 1, "obj(visit)->set(col, value): works");
    $obj3 = $rep->get_object("test_visit", "BOS,1,1980-08-31");
    is($obj3->{occasion},"woke up in the morning", "obj(visit).occasion successfully changed");
}

{
    # This is experimental. It should not be accepted as part of the official API yet.
    # In other words... Use the constructors on App::Repository, not the new() constructor on the class.
    my $obj = App::Moose::RepositoryObject->new(_table => "test_person", _key => 1);
    isa_ok($obj, "App::Moose::RepositoryObject", "non-factory constructor works");
    is($obj->{_key}, 1, "new() sets the _key");
}

{
    &drop_table($rep, "test_person");
    &drop_table($rep, "test_visit");
    &drop_table($rep, "test_city");
}

exit 0;

